package cn.edu.sgu.www.mhxysy.service.prop.impl;

import cn.edu.sgu.www.mhxysy.consts.CustomMessages;
import cn.edu.sgu.www.mhxysy.dto.prop.PropDTO;
import cn.edu.sgu.www.mhxysy.entity.prop.Prop;
import cn.edu.sgu.www.mhxysy.entity.prop.PropCategory;
import cn.edu.sgu.www.mhxysy.entity.repository.*;
import cn.edu.sgu.www.mhxysy.enums.PropStates;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.mapper.prop.PropCategoryMapper;
import cn.edu.sgu.www.mhxysy.mapper.prop.PropMapper;
import cn.edu.sgu.www.mhxysy.mapper.repository.*;
import cn.edu.sgu.www.mhxysy.pager.Pager;
import cn.edu.sgu.www.mhxysy.pager.prop.PropPager;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.service.prop.PropService;
import cn.edu.sgu.www.mhxysy.util.ObjectUtils;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import cn.edu.sgu.www.mhxysy.vo.Imager;
import cn.edu.sgu.www.mhxysy.vo.prop.PropVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Service
public class PropServiceImpl implements PropService {

    private final PropMapper propMapper;
    private final PackageGridMapper packageGridMapper;
    private final PackagePropMapper packagePropMapper;
    private final PropCategoryMapper propCategoryMapper;
    private final RolePackageMapper roleRepositoryMapper;
    private final RepositoryGridMapper repositoryGridMapper;
    private final RepositoryPropMapper repositoryPropMapper;

    @Autowired
    public PropServiceImpl(
            PropMapper propMapper,
            PackageGridMapper packageGridMapper,
            PackagePropMapper packagePropMapper,
            PropCategoryMapper propCategoryMapper,
            RolePackageMapper roleRepositoryMapper,
            RepositoryGridMapper repositoryGridMapper,
            RepositoryPropMapper repositoryPropMapper) {
        this.propMapper = propMapper;
        this.packageGridMapper = packageGridMapper;
        this.packagePropMapper = packagePropMapper;
        this.propCategoryMapper = propCategoryMapper;
        this.roleRepositoryMapper = roleRepositoryMapper;
        this.repositoryGridMapper = repositoryGridMapper;
        this.repositoryPropMapper = repositoryPropMapper;
    }

    @Override
    public void move(String propId, String repositoryId) {
        // 查询道具信息
        Prop prop = propMapper.selectById(propId);

        // 得到物品状态
        Integer state = prop.getState();

        if (PropStates.PACKAGE.getValue().equals(state)) {
            /*
             * 物品在包裹，存放到仓库
             */

            // 验证仓库ID的值
            ObjectUtils.requireNonNull(repositoryId, "仓库" + CustomMessages.ID_NOT_NULL);

            // 1、删除包裹道具
            Map<String, Object> columnMap = new HashMap<>();

            columnMap.put("prop_id", propId);

            packagePropMapper.deleteByMap(columnMap);

            // 2、添加仓库道具
            // 查询仓库的所有仓库格子的ID字段
            QueryWrapper<RepositoryGrid> queryWrapper = new QueryWrapper<>();

            queryWrapper.select("id", "location");
            queryWrapper.orderByAsc("location");
            queryWrapper.eq("repository_id", repositoryId);

            List<RepositoryGrid> list = repositoryGridMapper.selectList(queryWrapper);

            // 得到最近的没有存放道具的仓库格子
            Integer location = null;
            String gridId = null;

            for (RepositoryGrid repositoryGrid : list) {
                // 通过仓库格子ID查询仓库道具
                QueryWrapper<RepositoryProp> querywrapper = new QueryWrapper<>();

                querywrapper.eq("grid_id", repositoryGrid.getId());

                RepositoryProp repositoryProp = repositoryPropMapper.selectOne(querywrapper);

                if (repositoryProp == null) {
                    location = repositoryGrid.getLocation();
                    gridId = repositoryGrid.getId();

                    break;
                }
            }

            // 添加仓库道具
            if (gridId == null || location == null) {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "仓库已经满了，放不下了哦~");
            }

            RepositoryProp repositoryProp = new RepositoryProp();

            repositoryProp.setId(repositoryProp.uuid());
            repositoryProp.setPropId(propId);
            repositoryProp.setGridId(gridId);

            repositoryPropMapper.insert(repositoryProp);

            // 3、修改道具状态
            UpdateWrapper<Prop> updateWrapper = new UpdateWrapper<>();

            updateWrapper.eq("id", propId);
            updateWrapper.eq("state", PropStates.PACKAGE.getValue());

            updateWrapper.set("location", location);
            updateWrapper.set("state", PropStates.REPOSITORY.getValue());

            int update = propMapper.update(updateWrapper.getEntity(), updateWrapper);

            if (update == 0) {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "道具状态更新失败，请刷新页面后重试！");
            }
        } else if (PropStates.REPOSITORY.getValue().equals(state)) {
            /*
             * 物品在仓库，取回包裹
             */

            // 1、删除仓库道具
            QueryWrapper<RepositoryProp> queryWrapper = new QueryWrapper<>();

            queryWrapper.eq("prop_id", propId);

            int delete = repositoryPropMapper.delete(queryWrapper);

            if (delete == 0) {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "数据更新失败，请刷新页面后重试！");
            }

            // 2、添加包裹道具
            // 查询角色包裹
            QueryWrapper<RolePackage> queryWrapper1 = new QueryWrapper<>();

            queryWrapper1.eq("role_id", prop.getRoleId());

            RolePackage rolePackage = roleRepositoryMapper.selectOne(queryWrapper1);

            // 通过包裹ID查询包裹格子
            QueryWrapper<PackageGrid> queryWrapper2 = new QueryWrapper<>();

            queryWrapper2.eq("package_id", rolePackage.getId());
            queryWrapper2.orderByAsc("location");

            List<PackageGrid> packageGrids = packageGridMapper.selectList(queryWrapper2);

            // 查找最近一个没有存放物品的包裹格子
            Integer location = null;
            String gridId = null;

            for (PackageGrid packageGrid : packageGrids) {
                QueryWrapper<PackageProp> queryWrapper3 = new QueryWrapper<>();

                queryWrapper3.eq("grid_id", packageGrid.getId());

                PackageProp packageProp = packagePropMapper.selectOne(queryWrapper3);

                if (packageProp == null) {
                    location = packageGrid.getLocation();
                    gridId = packageGrid.getId();

                    break;
                }
            }

            if (gridId == null || location == null) {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "包裹已经满了，放不下了哦~");
            }

            PackageProp packageProp = new PackageProp();

            packageProp.setId(null);
            packageProp.setGridId(gridId);
            packageProp.setPropId(propId);

            packagePropMapper.insert(packageProp);

            // 3、修改道具状态
            UpdateWrapper<Prop> updateWrapper = new UpdateWrapper<>();

            updateWrapper.eq("id", propId);
            updateWrapper.eq("state", PropStates.REPOSITORY.getValue());

            updateWrapper.set("location", location);
            updateWrapper.set("state", PropStates.PACKAGE.getValue());

            int update = propMapper.update(updateWrapper.getEntity(), updateWrapper);

            if (update == 0) {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "道具状态更新失败，请刷新页面后重试！");
            }
        }
    }

    @Override
    public void insert(PropDTO propDTO) {
        /*
         * 1、添加道具
         */
        String note = null;
        String image = null;
        boolean isTheme = false; // 是否主题表道具

        Prop prop = toEntity(propDTO);

        // 得到并解析道具类型ID
        String propId = prop.getPropId();

        if (propId.contains("_")) { // 主题表道具
            // 道具名称和道具图片通过选择的道具类型ID查询得到
            int index = propId.lastIndexOf("_");

            String tableName = propId.substring(0, index);
            String primaryKey = propId.substring(index + 1);

            Imager imager = propMapper.selectProp(tableName, primaryKey);

            if (imager != null) {
                image = imager.getImage();
                note = imager.getNote();

                prop.setName(imager.getName());
            }

            propId = propDTO.getParentId();
            isTheme = true;
        } else { // 普通道具
            PropCategory propCategory = propCategoryMapper.selectById(propId);

            prop.setExtraNote(propCategory.getExtraNote());
            prop.setName(propCategory.getName());

            propId = propCategory.getParentId();
            image = propCategory.getImage();
            note = propCategory.getNote();
        }

        prop.setId(prop.uuid());
        prop.setState(PropStates.PACKAGE.getValue());

        // 查询角色包裹
        QueryWrapper<RolePackage> queryWrapper = new QueryWrapper<>();

        queryWrapper.eq("role_id", prop.getRoleId());

        RolePackage rolePackage = roleRepositoryMapper.selectOne(queryWrapper);

        // 通过包裹ID查询包裹格子
        QueryWrapper<PackageGrid> queryWrapper1 = new QueryWrapper<>();

        queryWrapper1.eq("package_id", rolePackage.getId());
        queryWrapper1.orderByAsc("location");

        List<PackageGrid> packageGrids = packageGridMapper.selectList(queryWrapper1);

        // 查找最近一个没有存放物品的包裹格子
        Integer location = null;
        String gridId = null;

        for (PackageGrid packageGrid : packageGrids) {
            QueryWrapper<PackageProp> queryWrapper2 = new QueryWrapper<>();

            queryWrapper2.eq("grid_id", packageGrid.getId());

            PackageProp packageProp = packagePropMapper.selectOne(queryWrapper2);

            if (packageProp == null) {
                location = packageGrid.getLocation();
                gridId = packageGrid.getId();

                break;
            }
        }

        if (gridId == null || location == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "包裹已经满了，放不下了哦~");
        }

        /*
         * 设置分类名称
         */
        List<String> propNames = getPropNames(propId);

        StringBuilder sortName = new StringBuilder();

        if (propNames.size() > 0) {
            // 倒序遍历，连接所有道具类型名称
            for (int i = propNames.size() - 1; i >= 0; i--) {
                String propName = propNames.get(i);

                sortName.append(propName).append("-");
            }
        }

        sortName.append(prop.getName());

        prop.setSortName(sortName.toString());

        /*
         * 设置道具图片
         */
        String propImage = getPropImage(propId);

        if (propImage != null) {
            image = propImage;
        }

        prop.setImage(image);

        /*
         * 设置类型
         */
        String propType = getPropType(propId);

        if (propType != null) {
            prop.setType(propType);
        }

        /*
         * 设置说明
         */
        if (StringUtils.isNullOrEmpty(note)) {
            note = getPropNote(propId);
        }

        prop.setNote(note);

        /*
         * 额外说明
         */
        if (isTheme) {
            String extraNote = getExtraNote(propId);

            if (extraNote != null) {
                prop.setExtraNote(extraNote);
            }
        }

        // 保存数据
        prop.setLocation(location);

        propMapper.insert(prop);

        /*
         * 2、添加包裹道具
         */
        PackageProp packageProp = new PackageProp();

        packageProp.setId(null);
        packageProp.setGridId(gridId);
        packageProp.setPropId(prop.getId());

        packagePropMapper.insert(packageProp);
    }

    @Override
    public void deleteById(String propId) {
        // 通过ID查询道具信息
        Prop prop = propMapper.selectById(propId);

        // 道具在包裹、仓库中，则删除包裹、仓库中的道具
        Integer state = prop.getState();

        if (PropStates.PACKAGE.getValue().equals(state)) {
            // 删除包裹里的道具
            QueryWrapper<PackageProp> queryWrapper = new QueryWrapper<>();

            queryWrapper.eq("prop_id", propId);

            packagePropMapper.delete(queryWrapper);
        } else if (PropStates.REPOSITORY.getValue().equals(state)) {
            // 删除仓库里的道具
            QueryWrapper<RepositoryProp> queryWrapper = new QueryWrapper<>();

            queryWrapper.eq("prop_id", propId);

            repositoryPropMapper.delete(queryWrapper);
        }

        // 删除道具
        propMapper.deleteById(propId);
    }

    @Override
    public void updateById(PropDTO propDTO) {
        ObjectUtils.requireNonNull(propDTO.getId(), CustomMessages.ID_NOT_NULL);

        Prop prop = toEntity(propDTO);

        propMapper.updateById(prop);
    }

    @Override
    public PropVO selectProp(String propId) {
        // 通过ID查询道具
        Prop prop = propMapper.selectById(propId);

        if (prop != null) {
            // 构建返回结果对象
            PropVO propVO = new PropVO();

            propVO.setId(prop.getId());
            propVO.setDays(prop.getDays());
            propVO.setName(prop.getName());
            propVO.setNote(prop.getNote());
            propVO.setType(prop.getType());
            propVO.setImage(prop.getImage());
            propVO.setState(prop.getState());
            propVO.setQuality(prop.getQuality());
            propVO.setLocation(prop.getLocation());
            propVO.setBindState(prop.getBindState());
            propVO.setExtraNote(prop.getExtraNote());
            propVO.setSubscript(prop.getSubscript());
            propVO.setDurability(prop.getDurability());

            return propVO;
        }

        return null;
    }

    @Override
    public Page<Prop> selectByPage(PropPager pager) {
        QueryWrapper<Prop> queryWrapper = new QueryWrapper<>();
        Page<Prop> page = Pager.ofPage(pager);

        queryWrapper.eq(
                StringUtils.isNotEmpty(pager.getRoleId()),
                "role_id", pager.getRoleId()
        );

        return propMapper.selectPage(page, queryWrapper);
    }

    private Prop toEntity(PropDTO propDTO) {
        Prop prop = new Prop();
        
        prop.setId(propDTO.getId());
        prop.setDays(propDTO.getDays());
        prop.setPropId(propDTO.getPropId());
        prop.setRarity(propDTO.getRarity());
        prop.setRoleId(propDTO.getRoleId());

        Integer quality = propDTO.getQuality();

        prop.setQuality(quality);

        if (quality != null) {
            prop.setSubscript(quality + "品");
        }

        prop.setBindState(propDTO.getBindState());
        prop.setDurability(propDTO.getDurability());

        // 空值处理，防止更新为空字符串
        String dataId = propDTO.getDataId();
        String tableName = propDTO.getTableName();

        prop.setDataId(StringUtils.isNotEmpty(dataId) ? dataId : null);
        prop.setTableName(StringUtils.isNotEmpty(tableName) ? tableName : null);

        return prop;
    }

    /**
     * 递归得到所有道具类型名称
     * @param propId 道具类型ID
     * @return List<String>
     */
    private List<String> getPropNames(String propId) {
        boolean flag = true;
        PropCategory propCategory;
        List<String> list = new ArrayList<>();

        while (flag) {
            propCategory = propCategoryMapper.selectById(propId);

            if (propCategory == null) {
                flag = false;

                continue;
            }

            list.add(propCategory.getName());

            String parentId = propCategory.getParentId();

            if (StringUtils.isNotEmpty(parentId)) {
                propCategory = propCategoryMapper.selectById(parentId);

                parentId = propCategory.getParentId();

                list.add(propCategory.getName());

                propId = parentId;
            } else {
                flag = false;
            }
        }

        return list;
    }

    /**
     * 递归得到道具图片
     * @param propId 道具类型ID
     * @return List<String>
     */
    private String getPropImage(String propId) {
        boolean flag = true;
        String propImage = null;
        PropCategory propCategory;

        while (flag) {
            propCategory = propCategoryMapper.selectById(propId);

            if (propCategory == null) {
                flag = false;

                continue;
            }

            String image = propCategory.getImage();

            if (StringUtils.isNotEmpty(image)) {
                propImage = image;
            }

            String parentId = propCategory.getParentId();

            if (StringUtils.isNotEmpty(parentId)) {
                propCategory = propCategoryMapper.selectById(parentId);

                image = propCategory.getImage();

                if (StringUtils.isNotEmpty(image)) {
                    propImage = image;
                }

                propId = parentId;
            } else {
                flag = false;
            }
        }

        return propImage;
    }

    /**
     * 递归得到类型
     * @param propId 道具类型ID
     * @return String
     */
    private String getPropType(String propId) {
        boolean flag = true;
        String propType = null;
        PropCategory propCategory;

        while (flag) {
            propCategory = propCategoryMapper.selectById(propId);

            if (propCategory == null) {
                flag = false;

                continue;
            }

            String type = propCategory.getPropType();

            if (StringUtils.isNotEmpty(type)) {
                propType = type;
            }

            String parentId = propCategory.getParentId();

            if (StringUtils.isNotEmpty(parentId)) {
                propCategory = propCategoryMapper.selectById(parentId);

                type = propCategory.getPropType();

                if (StringUtils.isNotEmpty(type)) {
                    propType = type;
                }

                propId = parentId;
            } else {
                flag = false;
            }
        }

        return propType;
    }

    /**
     * 递归得到道具说明
     * @param propId 道具类型ID
     * @return String
     */
    private String getPropNote(String propId) {
        boolean flag = true;
        String propNote = null;
        PropCategory propCategory;

        while (flag) {
            propCategory = propCategoryMapper.selectById(propId);

            if (propCategory == null) {
                flag = false;

                continue;
            }

            String note = propCategory.getNote();

            if (StringUtils.isNotEmpty(note)) {
                propNote = note;
            }

            String parentId = propCategory.getParentId();

            if (StringUtils.isNotEmpty(parentId)) {
                propCategory = propCategoryMapper.selectById(parentId);
                parentId = propCategory.getParentId();
                note = propCategory.getNote();

                if (StringUtils.isNotEmpty(note)) {
                    propNote = note;
                }

                propId = parentId;
            } else {
                flag = false;
            }
        }

        return propNote;
    }

    /**
     * 递归得到道具额外说明
     * @param propId 道具类型ID
     * @return String
     */
    private String getExtraNote(String propId) {
        boolean flag = true;
        String propExtraNote = null;
        PropCategory propCategory;

        while (flag) {
            propCategory = propCategoryMapper.selectById(propId);

            if (propCategory == null) {
                flag = false;

                continue;
            }

            String extraNote = propCategory.getExtraNote();

            if (StringUtils.isNotEmpty(extraNote)) {
                propExtraNote = extraNote;
            }

            String parentId = propCategory.getParentId();

            if (StringUtils.isNotEmpty(parentId)) {
                propCategory = propCategoryMapper.selectById(parentId);
                parentId = propCategory.getParentId();
                extraNote = propCategory.getExtraNote();

                if (StringUtils.isNotEmpty(extraNote)) {
                    propExtraNote = extraNote;
                }

                propId = parentId;
            } else {
                flag = false;
            }
        }

        return propExtraNote;
    }

}